package com.xuecheng.orders.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.base.utils.IdWorkerUtils;
import com.xuecheng.base.utils.QRCodeUtil;
import com.xuecheng.messagesdk.model.po.MqMessage;
import com.xuecheng.messagesdk.service.MqMessageService;
import com.xuecheng.orders.config.AlipayConfig;
import com.xuecheng.orders.config.PayNotifyConfig;
import com.xuecheng.orders.mapper.XcOrdersGoodsMapper;
import com.xuecheng.orders.mapper.XcOrdersMapper;
import com.xuecheng.orders.mapper.XcPayRecordMapper;
import com.xuecheng.orders.model.dto.AddOrderDto;
import com.xuecheng.orders.model.dto.PayRecordDto;
import com.xuecheng.orders.model.dto.PayStatusDto;
import com.xuecheng.orders.model.po.XcOrders;
import com.xuecheng.orders.model.po.XcOrdersGoods;
import com.xuecheng.orders.model.po.XcPayRecord;
import com.xuecheng.orders.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Correlation;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    //订单表
    @Autowired
    private XcOrdersMapper xcOrdersMapper;

    @Autowired
    private XcPayRecordMapper xcPayRecordMapper;

    //订单明细表
    @Autowired
    private XcOrdersGoodsMapper xcOrdersGoodsMapper;

    @Autowired
    private OrderServiceImpl proxy;

    @Value("${pay.qrcodeurl}")
    private String qrcodeurl;

    @Value("${pay.alipay.APP_ID}")
    private String APP_ID;

    @Value("${pay.alipay.APP_PRIVATE_KEY}")
    String APP_PRIVATE_KEY;

    @Value("${pay.alipay.ALIPAY_PUBLIC_KEY}")
    String ALIPAY_PUBLIC_KEY;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private MqMessageService mqMessageService;

    @Transactional
    @Override
    public PayRecordDto createOrder(String userId, AddOrderDto addOrderDto) {
        //插入信息,订单主表和订单明细表
        XcOrders xcOrders = saveXcOrders(userId, addOrderDto);

        //插入支付记录
        XcPayRecord payRecord = createPayRecord(xcOrders);
        Long payNo = payRecord.getPayNo();

        //生成二维码
        QRCodeUtil qrCodeUtil = new QRCodeUtil();
        String url = String.format(qrcodeurl, payNo);
        String qrCode = null;
        try {
            qrCode = qrCodeUtil.createQRCode(url, 200, 200);
        } catch (IOException e) {
            XueChengPlusException.cast("生成二维码出错");
        }

        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecord, payRecordDto);
        payRecordDto.setQrcode(qrCode);
        return payRecordDto;
    }

    /**
     * 插入支付记录表
     * @param xcOrders
     * @return
     */
    private XcPayRecord createPayRecord(XcOrders xcOrders){
        //订单id
        Long orderId = xcOrders.getId();

        //判断订单是否存在
        XcOrders orders = xcOrdersMapper.selectById(orderId);
        if(orders == null){
            XueChengPlusException.cast("订单不存在");
        }

        //订单状态
        String status = orders.getStatus();
        if(status.equals("601002")){
            XueChengPlusException.cast("订单已支付成功");
        }

        //添加支付记录
        XcPayRecord payRecord = new XcPayRecord();
        payRecord.setPayNo(IdWorkerUtils.getInstance().nextId()); //支付记录号，要传给支付宝
        payRecord.setOrderId(orders.getId());//商品订单号
        payRecord.setOrderName(orders.getOrderName());
        payRecord.setTotalPrice(orders.getTotalPrice());
        payRecord.setCurrency("CNY");
        payRecord.setCreateDate(LocalDateTime.now());
        payRecord.setStatus("601001");//未支付
        payRecord.setUserId(orders.getUserId());
        int insert = xcPayRecordMapper.insert(payRecord);
        if(insert <= 0){
            XueChengPlusException.cast("插入支付记录表失败");
        }
        return payRecord;

    }

    /**
     * 保存订单信息
     * @param userId
     * @param addOrderDto
     * @return
     */
    private XcOrders saveXcOrders(String userId, AddOrderDto addOrderDto){
        //插入订单表，订单主表和订单明细表
        //进行幂等性判断
        XcOrders order = getOrderByBusinessId(addOrderDto.getOutBusinessId());
        if(order != null){
            return order;
        }

        //插入订单主表
        order = new XcOrders();
        //根据雪花算法生成订单号
        order.setId(IdWorkerUtils.getInstance().nextId());
        order.setTotalPrice(addOrderDto.getTotalPrice());
        order.setCreateDate(LocalDateTime.now());
        order.setStatus("600001");//未支付
        order.setUserId(userId);
        order.setOrderType(addOrderDto.getOrderType());
        order.setOrderName(addOrderDto.getOrderName());
        order.setOrderDetail(addOrderDto.getOrderDetail());
        order.setOrderDescrip(addOrderDto.getOrderDescrip());
        order.setOutBusinessId(addOrderDto.getOutBusinessId());//选课记录id
        int insert = xcOrdersMapper.insert(order);
        if(insert <= 0){
            XueChengPlusException.cast("添加订单失败");
        }

        //上面插入后的订单id
        Long orderId = order.getId();
        //插入订单明细表
        //将前端传入的明细json转为List
        String orderDetailJson = order.getOrderDetail();
        List<XcOrdersGoods> xcOrdersGoods = JSON.parseArray(orderDetailJson, XcOrdersGoods.class);
        xcOrdersGoods.forEach(goods -> {
            goods.setOrderId(orderId);
            //插入订单明细表
            xcOrdersGoodsMapper.insert(goods);
        });
        return order;
    }

    /**
     * 根据业务ID查询订单，业务ID就是选课记录表中的主键
     * @param businessId
     * @return
     */
    private XcOrders getOrderByBusinessId(String businessId){
        LambdaQueryWrapper<XcOrders> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(XcOrders::getOutBusinessId, businessId);
        return xcOrdersMapper.selectOne(queryWrapper);
    }

    /**
     * 根据记录号判断支付记录表中是否存在
     * @param payNo
     * @return
     */
    @Override
    public XcPayRecord getPayRecordByPayNo(String payNo) {
        LambdaQueryWrapper<XcPayRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(XcPayRecord::getPayNo, payNo);
        XcPayRecord xcPayRecord = xcPayRecordMapper.selectOne(queryWrapper);
        return xcPayRecord;
    }

    /**
     * 查询支付结果,拿到结果后更新支付记录表和订单表中的支付状态
     * @param payNo 支付记录号
     * @return
     */
    @Override
    public PayRecordDto queryPayResult(String payNo) {
        //查询支付结果
        PayStatusDto payStatusDto = queryPayResultFromAliPay(payNo);

        //更新支付记录表和订单表中的支付状态
        proxy.saveAliPayStatus(payStatusDto);

        XcPayRecord payRecordByPayNo = getPayRecordByPayNo(payNo);
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecordByPayNo, payRecordDto);
        return payRecordDto;
    }

    /**
     * 调用支付宝接口查询支付结果
     * @param payNo 支付记录号
     * @return
     */
    private PayStatusDto queryPayResultFromAliPay(String payNo){
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, APP_ID, APP_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE);
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", payNo);
        //bizContent.put("trade_no", "2014112611001004680073956707");
        request.setBizContent(bizContent.toString());
        String body = null;
        try {
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if(!response.isSuccess()){
                XueChengPlusException.cast("请求支付宝支付结果失败");
            }
            body = response.getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
            XueChengPlusException.cast("请求支付宝查询支付结果异常");
        }
        Map map = JSON.parseObject(body, Map.class);
        Map alipay_trade_query_response = (Map) map.get("alipay_trade_query_response");

        PayStatusDto payStatusDto = new PayStatusDto();
        //解析支付结果
        payStatusDto.setTrade_status((String) alipay_trade_query_response.get("trade_status"));
        payStatusDto.setOut_trade_no(payNo);
        payStatusDto.setTrade_no((String) alipay_trade_query_response.get("trade_no"));
        payStatusDto.setApp_id(APP_ID);
        payStatusDto.setTotal_amount((String) alipay_trade_query_response.get("total_amount"));
        return payStatusDto;
    }

    /**
     * 保存支付结果
     * @param payStatusDto 支付结果信息
     */
    @Transactional
    public void saveAliPayStatus(PayStatusDto payStatusDto){
        String payNo = payStatusDto.getOut_trade_no();
        XcPayRecord payRecordByPayNo = getPayRecordByPayNo(payNo);
        if(payRecordByPayNo == null){
            XueChengPlusException.cast("找不到相关支付记录");
        }

        //拿到相关联的订单ID
        Long orderId = payRecordByPayNo.getOrderId();
        XcOrders xcOrders = xcOrdersMapper.selectById(orderId);
        if(xcOrders == null){
            XueChengPlusException.cast("找不到相关联的订单");
        }

        //从数据库查找是否已经成功
        String statusFromDb = payStatusDto.getTrade_status();
        if(statusFromDb.equals("601002")){
            //已经保存成功
            return;
        }

        //从支付宝查询到的支付结果
        String status = payStatusDto.getTrade_status();
        if(status.equals("TRADE_SUCCESS")){
            //更新支付记录表状态
            //更新状态为成功
            payRecordByPayNo.setStatus("601002");
            //更新支付宝的订单号
            payRecordByPayNo.setOutPayNo(payStatusDto.getTrade_no());
            //更新第三方支付渠道编号
            payRecordByPayNo.setOutPayChannel("AliPay");
            //更新支付成功的时间
            payRecordByPayNo.setPaySuccessTime(LocalDateTime.now());
            xcPayRecordMapper.updateById(payRecordByPayNo);

            //更新订单表状态
            //更新状态为成功
            xcOrders.setStatus("600002");
            xcOrdersMapper.updateById(xcOrders);

            //将消息保存到数据库(mq_message)
            MqMessage message = mqMessageService.addMessage("payresult_notify", xcOrders.getOutBusinessId(), xcOrders.getOrderType(), null);

            //发送消息
            notifyPayResult(message);
        }
    }

    @Override
    public void notifyPayResult(MqMessage mqMessage) {
        //消息内容
        String jsonString = JSON.toJSONString(mqMessage);

        //创建一个持久化消息
        Message message = MessageBuilder.withBody(jsonString.getBytes(StandardCharsets.UTF_8)).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();

        Long id = mqMessage.getId();
        //要指定一个全局的消息ID，保证不重复执行
        CorrelationData correlationData = new CorrelationData(id.toString());
        //使用correlationData指定回调方法
        correlationData.getFuture().addCallback(result -> {
            if(result.isAck()){
                //消息成功发送到了交换机
                log.debug("消息发送成功:{}", jsonString);
                //将消息从数据库表删除(mq_message)
                mqMessageService.completed(id);
            }else{
                //消息发送失败
                log.debug("消息发送失败:{}", jsonString);
            }

        }, ex -> {
            //发生异常（例如网络问题）
            log.debug("消息发送异常:{}", jsonString);
        });

        //发送消息
        rabbitTemplate.convertAndSend(PayNotifyConfig.PAYNOTIFY_EXCHANGE_FANOUT, "", message, correlationData);
    }
}
